import { G6 } from "@/g6/g6";

const Util = G6.Util;
const abs = Math.abs;
const DRAG_OFFSET = 10;
const body = document.body;
const ALLOW_EVENTS = [16, 17, 18];

export function dragCanvas(G6) {
  G6.registerBehavior("drag-canvas", {
    getDefaultCfg() {
      return {
        direction: "both",
      };
    },
    getEvents() {
      return {
        "canvas:mousedown": "onMouseDown",
        "canvas:mousemove": "onMouseMove",
        "canvas:mouseup": "onMouseUp",
        "canvas:click": "onMouseUp",
        "canvas:mouseleave": "onOutOfRange",
        keyup: "onKeyUp",
        keydown: "onKeyDown",
      };
    },
    updateViewport(e) {
      const origin = this.origin;
      const clientX = +e.clientX;
      const clientY = +e.clientY;
      if (isNaN(clientX) || isNaN(clientY)) {
        return;
      }
      let dx = clientX - origin.x;
      let dy = clientY - origin.y;
      if (this.get("direction") === "x") {
        dy = 0;
      } else if (this.get("direction") === "y") {
        dx = 0;
      }
      this.origin = {
        x: clientX,
        y: clientY,
      };
      this.graph.translate(dx, dy);
      this.graph.paint();
    },
    onMouseDown(e) {
      if (e.event.button === 2) {
        return;
      }
      if (this.keydown) {
        return;
      }

      this.origin = { x: e.clientX, y: e.clientY };
      this.dragging = false;
    },
    onMouseMove(e) {
      if (this.keydown) {
        return;
      }

      e = Util.cloneEvent(e);
      const graph = this.graph;
      if (!this.origin) {
        return;
      }
      if (this.origin && !this.dragging) {
        if (abs(this.origin.x - e.clientX) + abs(this.origin.y - e.clientY) < DRAG_OFFSET) {
          return;
        }
        if (this.shouldBegin.call(this, e)) {
          e.type = "dragstart";
          graph.emit("canvas:dragstart", e);
          this.dragging = true;
        }
      }
      if (this.dragging) {
        e.type = "drag";
        graph.emit("canvas:drag", e);
      }
      if (this.shouldUpdate.call(this, e)) {
        this.updateViewport(e);
      }
    },
    onMouseUp(e) {
      if (this.keydown) {
        return;
      }

      if (!this.dragging) {
        this.origin = null;
        return;
      }
      e = Util.cloneEvent(e);
      const graph = this.graph;
      if (this.shouldEnd.call(this, e)) {
        this.updateViewport(e);
      }
      e.type = "dragend";
      graph.emit("canvas:dragend", e);
      this.endDrag();
    },
    endDrag() {
      if (this.dragging) {
        this.origin = null;
        this.dragging = false;
        // 终止时需要判断此时是否在监听画布外的 mouseup 事件，若有则解绑
        const fn = this.fn;
        if (fn) {
          body.removeEventListener("mouseup", fn, false);
          this.fn = null;
        }
      }
    },
    // 若在拖拽时，鼠标移出画布区域，此时放开鼠标无法终止 drag 行为。在画布外监听 mouseup 事件，放开则终止
    onOutOfRange(e) {
      if (this.dragging) {
        const self = this;
        const canvasElement = self.graph.get("canvas").get("el");
        const fn = (ev) => {
          if (ev.target !== canvasElement) {
            self.onMouseUp(e);
          }
        };
        this.fn = fn;
        body.addEventListener("mouseup", fn, false);
      }
    },
    onKeyDown(e) {
      const code = e.keyCode || e.which;
      if (ALLOW_EVENTS.indexOf(code) > -1) {
        this.keydown = true;
      } else {
        this.keydown = false;
      }
    },
    onKeyUp() {
      this.keydown = false;
    },
  });
}
